
import ForceGraph3D from '3d-force-graph'
import * as THREE from 'three'
import SpriteText from "three-spritetext"
import * as TWEEN from '@tweenjs/tween.js'
// import { gsap } from "https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js"
interface ChartOptions {
    container: HTMLElement,
    options: {
        // gData: {
        //     nodes: {},
        //     links: {}
        // }
    }
}

// 开场动画
function cameraCenter(Graph: any, x: any = 0, y: any = 0, z: any = -1000) {
    Graph.cameraPosition({
        x: x,
        y: y,
        z: z,
    })
    Graph.cameraPosition(
        { x: 0, y: 0, z: 1000, },
        { x: 0, y: 0, z: 0 }
        , 2000)
}

// 更新hover样式
function updateHighlight(Graph: any) {
    Graph
        .nodeColor(Graph.nodeColor())
        .linkWidth(Graph.linkWidth())
        .linkDirectionalParticles(Graph.linkDirectionalParticles());
}

// 更新显隐
function updateVisible(Graph: any) {
    Graph
        .nodeVisibility(Graph.nodeVisibility())
        .linkVisibility(Graph.linkVisibility())
    // .nodeThreeObject(Graph.nodeThreeObject())
}

export default class Graph3D {
    options: any = {}
    container?: HTMLElement = undefined
    myGraph: any = null  // 3d图实例
    hoverNode: any = null // hover的节点
    activeNode: any = null  // 选中的节点
    tweenArr: Array<any> = []

    constructor({ container, options = {} }: ChartOptions) {
        this.options = options
        this.container = container
        this.init()
    }

    init() {
        let cameraPosition: any = null
        let cameraTween: any = null
        let tweenGroup: TWEEN.Group = new TWEEN.Group()
        // this.myGraph = new ForceGraph3D(document.getElementById('graphContainer'))
        let { gData } = this.options
        let relatedLinks: any = []
        let relatedNodes: any = []

        gData.nodes.forEach((node: any) => {
            if (node.cj == '0') {
                node.r = 20
                node.Resolution = 12
            } else if (node.cj == '1') {
                node.r = 10
                node.Resolution = 24
            } else {
                node.r = 3
                node.Resolution = 24
            }
        })

        let myGraph = new ForceGraph3D(this.container?.value, {
            controlType: 'trackball'
        })
        this.myGraph = myGraph

        myGraph.enableNodeDrag(false) // 禁止拖拽
            .showNavInfo(false) // 隐藏右下角的导航信息
            // .backgroundColor('#fff')
            .graphData(gData)
            // .nodeAutoColorBy('courseId') // 根据节点的courseId属性自动着色
            // .nodeColor((node) => {
            //     const degree = myGraph.graphData().links.filter(link => link.source === node.id || link.target === node.id).length;
            //     return degree > 10 ? 'red' : 'blue';
            // })
            // .nodeColor(node => {
            //     let findNode = relatedNodes.find((n: any) => n.id == node.id)
            //     let color = 'rgba(0,255,255,0.6)'
            //     if (this.activeNode || !findNode) { // 选中节点、取消hover
            //         return color
            //     } else { // hover节点
            //         return node.id === this.hoverNode?.id ? 'rgb(255,0,0,1)' : 'rgba(255,160,0,0.8)'
            //     }
            // })
            // .nodeResolution(8) // 节点分辨率
            .nodeLabel((node: any) => {
                return `<p style="color: #fff;padding: 10px;background: #00000040;">${node.name}<p>`
            })
            .nodeVal((node: any) => node.r)
            .nodeVisibility((node: any) => {
                // 节点文字是否显示
                if (node.__threeObj) {
                    if (this.activeNode) { // 选中节点
                        node.__threeObj.getObjectByName('textBox').visible = true
                    } else {
                        if (node.cj == '0' || node.cj == '1') {
                            node.__threeObj.getObjectByName('textBox').visible = true
                        } else {
                            node.__threeObj.getObjectByName('textBox').visible = false
                        }
                    }
                }
                // 是否显示节点
                if (!this.activeNode) {
                    return true
                }
                return relatedNodes.find((n: any) => n.id == node.id) ? true : false
            })

            // .nodeThreeObjectExtend(true)
            .nodeThreeObject((node: any) => {
                // imgTexture = new THREE.TextureLoader().load(require('./assets/5.png'))
                const sprite: any = new SpriteText(node.name)
                sprite.color = '#fff'
                sprite.name = 'textBox'
                if (node.cj == '0') {
                    sprite.textHeight = 8
                } else if (node.cj == '1') {
                    sprite.textHeight = 4
                } else {
                    sprite.textHeight = 4
                }
                sprite.strokeWidth = 1
                sprite.strokeColor = node.nodeColor
                sprite.padding = 1
                sprite.material.depthWrite = false
                sprite.position.set(0, 0, node.r + 2)

                let resolution = node.cj == '0' ? 24 : 24
                const geometry = new THREE.SphereGeometry(node.r, resolution, resolution);
                const material = new THREE.MeshPhongMaterial({
                    color: node.nodeColor,
                    // 这里可以设置更多材质属性来实现颜色渐变等效果，比如添加顶点颜色等
                })
                const mesh = new THREE.Mesh(geometry, material)
                mesh.name = 'node'
                const group = new THREE.Group()
                group.add(mesh)
                group.add(sprite)
                sprite.visible = false
                if (this.activeNode) {
                    sprite.visible = true
                } else {
                    if (node.cj == '0' || node.cj == '1') {
                        sprite.visible = true
                    }
                }
                return group
            })
            .onNodeHover((node: any) => {  // 悬停时节点变大
                gData.nodes.forEach((n: any) => {
                    const target = n.__threeObj && n.__threeObj.getObjectByName('node')
                    if (target && target.scale.x > 1) {
                        let nodeTween = new TWEEN.Tween(target.scale)
                            .to({ x: 1, y: 1, z: 1 }, 200)
                            .easing(TWEEN.Easing.Quadratic.Out)
                            .onComplete(() => {
                                tweenGroup.remove(nodeTween)
                            })
                            .start()
                        tweenGroup.add(nodeTween)
                    }
                })
                if (node) {
                    this.hoverNode = node
                    const target = node.__threeObj.getObjectByName('node');
                    if (target) {
                        let nodeTween = new TWEEN.Tween(target.scale)
                            .to({ x: 1.5, y: 1.5, z: 1 }, 200)
                            .easing(TWEEN.Easing.Quadratic.Out)
                            .onComplete(() => {
                                tweenGroup.remove(nodeTween)
                            })
                            .start()
                        tweenGroup.add(nodeTween)
                    }
                }
                if (this.activeNode) return
                this.hoverNode = null
                relatedLinks = []
                relatedNodes = []
                if (node) {
                    relatedLinks = gData.links.filter((link: any) => link.source.id === node.id || link.target.id === node.id)
                    // console.log('relatedLinks',relatedLinks)
                    gData.nodes.forEach((n: any) => {
                        let findNode = relatedLinks.find((link: any) => link.source.id === n.id || link.target.id === n.id)
                        findNode && relatedNodes.push(n)
                    })
                    // console.log('relatedNodes',relatedNodes)
                }
                updateHighlight(myGraph)
            })
            // 节点点击
            .onNodeClick((node: any) => {
                // console.log('onNodeClick', node)
                if (this.activeNode && node.id == this.activeNode.id) { // 重复点击
                    return
                }
                cameraTween && cameraTween.stop()
                cameraPosition = myGraph.cameraPosition()
                relatedLinks = []
                relatedNodes = []
                if (node) {
                    this.activeNode = node
                    relatedLinks = gData.links.filter((link: any) => link.source.id === node.id || link.target.id === node.id)
                    gData.nodes.forEach((n: any) => {
                        let findNode = relatedLinks.find((link: any) => link.source.id === n.id || link.target.id === n.id)
                        findNode && relatedNodes.push(n)
                    })
                }
                const distance = 200
                const distRatio = 1 + distance / Math.hypot(node.x, node.y, node.z)
                const targetPosition = { x: node.x * distRatio, y: node.y * distRatio, z: node.z * distRatio }
                cameraTween = new TWEEN.Tween(myGraph.camera().position)
                    .to(targetPosition, 1000) // 1000毫秒移动到目标位置，可调整时间
                    .easing(TWEEN.Easing.Quadratic.Out) // 选择缓动函数
                    .onUpdate(() => {
                        // myGraph.camera().lookAt({ x: 0, y: 0, z: 0 })
                    })
                    .onComplete(() => {
                        // tweenGroup.remove(tween)
                        updateVisible(myGraph)
                    }).start()
            })
            // 背景点击
            .onBackgroundClick((event) => {
                // console.log('onBackgroundClick', myGraph.cameraPosition())
                if(!this.activeNode) return
                cameraTween && cameraTween.stop()
                this.activeNode = null
                cameraTween = new TWEEN.Tween(myGraph.camera().position)
                    .to(cameraPosition, 2000) // 1000毫秒移动到目标位置，可调整时间
                    .easing(TWEEN.Easing.Quadratic.Out) // 选择缓动函数
                    .onUpdate(() => {
                        // myGraph.camera().lookAt({ x: 0, y: 0, z: 0 })
                    })
                    .onComplete(() => {
                        relatedLinks = []
                        relatedNodes = []
                        updateVisible(myGraph)
                        // tweenGroup.remove(tween)
                    }).start()
            })
            // 方向箭头
            // .linkDirectionalArrowLength(1.5)
            // .linkDirectionalArrowRelPos(1)
            // .linkCurvature(0.25)
            // .linkOpacity(0.2)  // 线透明度 默认0.2
            .linkWidth(link => 1)
            .linkDirectionalParticles(link => {
                return relatedLinks.find((l: any) => l.index == link.index) ? 1 : 0
            })
            .linkDirectionalParticleWidth(4)
            .linkVisibility(link => {
                if (!this.activeNode) {
                    return true
                }
                return relatedLinks.find((l: any) => l.index == link.index) ? true : false
            })

        // cameraCenter(myGraph)
        // 渲染循环，用于更新Tween.js动画以及重绘图形
        function animate() {
            requestAnimationFrame(animate)
            cameraTween && cameraTween.update()
            tweenGroup.update()
            gData.nodes.forEach((node: any) => {
                const threeObj = node.__threeObj
                threeObj && threeObj.lookAt(myGraph.camera().position)
            })
            // Graph.render();
        }
        animate()
    }
}

// function resizeGraph() {
// 	if (graph) {
// 		var height = document.getElementById("3d-graph").clientHeight;
// 		var width = document.getElementById("3d-graph").clientWidth;

// 		graph.width(width);
// 		graph.height(height);
// 		graph.controls().handleResize();
// 	}
// }